#ifndef _WX_OBJECTH__
#define _WX_OBJECTH__

#include "wx/memory.h"

class wxObject;
class wxClassInfo;
class wxHashTable;
class wxObjectRefData;

typedef wxObject *( *wxObjectConstructorFn )( void );

class wxClassInfo {
  public:
    wxClassInfo( const wxChar *className, const wxClassInfo *baseInfo1,
                 const wxClassInfo *baseInfo2, int size, wxObjectConstructorFn ctor )
      : m_className( className )
      , m_objectSize( size )
      , m_objectConstructor( ctor )
      , m_baseInfo1( baseInfo1 )
      , m_baseInfo2( baseInfo2 )
      , m_next( sm_first ) {
      sm_first = this;
      Register();
    }
    ~wxClassInfo();
    wxObject *CreateObject() const { return m_objectConstructor ? ( *m_objectConstructor )() : 0; }
    bool IsDynamic() const { return ( NULL != m_objectConstructor ); }
    const wxChar *GetClassName() const { return m_className; }
    const wxChar *GetBaseClassName1() const { return m_baseInfo1 ? m_baseInfo1->GetClassName() : NULL; }
    const wxChar *GetBaseClassName2() const { return m_baseInfo2 ? m_baseInfo2->GetClassName() : NULL; }
    const wxClassInfo  *GetBaseClass1() const { return m_baseInfo1; }
    const wxClassInfo  *GetBaseClass2() const { return m_baseInfo2; }
    int GetSize() const { return m_objectSize; }
    wxObjectConstructorFn GetConstructor() const { return m_objectConstructor; }
    static const wxClassInfo *GetFirst() { return sm_first; }
    const wxClassInfo *GetNext() const { return m_next; }
    static wxClassInfo *FindClass( const wxChar *className );
    bool IsKindOf( const wxClassInfo *info ) const {
      return info != 0 && ( info == this || ( m_baseInfo1 && m_baseInfo1->IsKindOf( info ) ) ||
                            ( m_baseInfo2 && m_baseInfo2->IsKindOf( info ) ) );
    }

  public:
    const wxChar *m_className;
    int m_objectSize;
    wxObjectConstructorFn m_objectConstructor;
    const wxClassInfo *m_baseInfo1;
    const wxClassInfo *m_baseInfo2;
    static wxClassInfo *sm_first;
    wxClassInfo *m_next;
    static wxHashTable *sm_classTable;
  private:
    static wxClassInfo *GetBaseByName( const wxChar *name );
    DECLARE_NO_COPY_CLASS( wxClassInfo )
  protected:
    void Register();
    void Unregister();
};

wxObject *wxCreateDynamicObject( const wxChar *name );

#define DECLARE_ABSTRACT_CLASS(name)                                          \
  public:                                                                   \
  static wxClassInfo ms_classInfo;                                      \
  virtual wxClassInfo *GetClassInfo() const;

#define DECLARE_DYNAMIC_CLASS_NO_ASSIGN(name)                                 \
  DECLARE_NO_ASSIGN_CLASS(name)                                             \
  DECLARE_DYNAMIC_CLASS(name)

#define DECLARE_DYNAMIC_CLASS_NO_COPY(name)                                   \
  DECLARE_NO_COPY_CLASS(name)                                               \
  DECLARE_DYNAMIC_CLASS(name)

#define DECLARE_DYNAMIC_CLASS(name)                                           \
  DECLARE_ABSTRACT_CLASS(name)                                              \
  static wxObject* wxCreateObject();

#define DECLARE_CLASS(name) DECLARE_DYNAMIC_CLASS(name)

#define wxIMPLEMENT_CLASS_COMMON(name, basename, baseclsinfo2, func)          \
  wxClassInfo name::ms_classInfo(wxT(#name),                                \
                                 &basename::ms_classInfo,                                          \
                                 baseclsinfo2,                                                     \
                                 (int) sizeof(name),                                               \
                                 (wxObjectConstructorFn) func);                                    \
  \
  wxClassInfo *name::GetClassInfo() const                                   \
  { return &name::ms_classInfo; }

#define wxIMPLEMENT_CLASS_COMMON1(name, basename, func)                       \
  wxIMPLEMENT_CLASS_COMMON(name, basename, NULL, func)

#define wxIMPLEMENT_CLASS_COMMON2(name, basename1, basename2, func)           \
  wxIMPLEMENT_CLASS_COMMON(name, basename1, &basename2::ms_classInfo, func)

#define IMPLEMENT_DYNAMIC_CLASS(name, basename)                               \
  wxIMPLEMENT_CLASS_COMMON1(name, basename, name::wxCreateObject)           \
  wxObject* name::wxCreateObject() { return new name; }


#define IMPLEMENT_DYNAMIC_CLASS2(name, basename1, basename2)   \
  wxIMPLEMENT_CLASS_COMMON2(name, basename1, basename2, name::wxCreateObject)   \
  wxObject* name::wxCreateObject() { return new name; }

#define IMPLEMENT_ABSTRACT_CLASS(name, basename)                              \
  wxIMPLEMENT_CLASS_COMMON1(name, basename, NULL)

#define IMPLEMENT_ABSTRACT_CLASS2(name, basename1, basename2)                 \
  wxIMPLEMENT_CLASS_COMMON2(name, basename1, basename2, NULL)

#define IMPLEMENT_CLASS IMPLEMENT_ABSTRACT_CLASS
#define IMPLEMENT_CLASS2 IMPLEMENT_ABSTRACT_CLASS2

#define _DECLARE_DL_SENTINEL(name, exportdecl)  \
  class exportdecl name##PluginSentinel {         \
    private:                                        \
      static const wxString sm_className;         \
    public:                                         \
      name##PluginSentinel();                     \
      ~name##PluginSentinel();                    \
  };                                              \
  name##PluginSentinel  m_pluginsentinel;

#define _IMPLEMENT_DL_SENTINEL(name)                                \
  const wxString name::name##PluginSentinel::sm_className(#name);    \
  name::name##PluginSentinel::name##PluginSentinel() {               \
    wxPluginLibrary *e = (wxPluginLibrary*) wxPluginLibrary::ms_classes.Get(#name);   \
    if( e != 0 ) { e->RefObj(); }                                      \
  }                                                                  \
  name::name##PluginSentinel::~name##PluginSentinel() {            \
    wxPluginLibrary *e = (wxPluginLibrary*) wxPluginLibrary::ms_classes.Get(#name);   \
    if( e != 0 ) { e->UnrefObj(); }                                 \
  }

#define DECLARE_PLUGGABLE_CLASS(name) \
  DECLARE_DYNAMIC_CLASS(name) _DECLARE_DL_SENTINEL(name, WXDLLEXPORT)
#define DECLARE_ABSTRACT_PLUGGABLE_CLASS(name)  \
  DECLARE_ABSTRACT_CLASS(name) _DECLARE_DL_SENTINEL(name, WXDLLEXPORT)

#define DECLARE_USER_EXPORTED_PLUGGABLE_CLASS(name, usergoo) \
  DECLARE_DYNAMIC_CLASS(name) _DECLARE_DL_SENTINEL(name, usergoo)
#define DECLARE_USER_EXPORTED_ABSTRACT_PLUGGABLE_CLASS(name, usergoo)  \
  DECLARE_ABSTRACT_CLASS(name) _DECLARE_DL_SENTINEL(name, usergoo)

#define IMPLEMENT_PLUGGABLE_CLASS(name, basename) \
  IMPLEMENT_DYNAMIC_CLASS(name, basename) _IMPLEMENT_DL_SENTINEL(name)
#define IMPLEMENT_PLUGGABLE_CLASS2(name, basename1, basename2)  \
  IMPLEMENT_DYNAMIC_CLASS2(name, basename1, basename2) _IMPLEMENT_DL_SENTINEL(name)
#define IMPLEMENT_ABSTRACT_PLUGGABLE_CLASS(name, basename) \
  IMPLEMENT_ABSTRACT_CLASS(name, basename) _IMPLEMENT_DL_SENTINEL(name)
#define IMPLEMENT_ABSTRACT_PLUGGABLE_CLASS2(name, basename1, basename2)  \
  IMPLEMENT_ABSTRACT_CLASS2(name, basename1, basename2) _IMPLEMENT_DL_SENTINEL(name)

#define IMPLEMENT_USER_EXPORTED_PLUGGABLE_CLASS(name, basename) \
  IMPLEMENT_PLUGGABLE_CLASS(name, basename)
#define IMPLEMENT_USER_EXPORTED_PLUGGABLE_CLASS2(name, basename1, basename2)  \
  IMPLEMENT_PLUGGABLE_CLASS2(name, basename1, basename2)
#define IMPLEMENT_USER_EXPORTED_ABSTRACT_PLUGGABLE_CLASS(name, basename) \
  IMPLEMENT_ABSTRACT_PLUGGABLE_CLASS(name, basename)
#define IMPLEMENT_USER_EXPORTED_ABSTRACT_PLUGGABLE_CLASS2(name, basename1, basename2)  \
  IMPLEMENT_ABSTRACT_PLUGGABLE_CLASS2(name, basename1, basename2)

#define CLASSINFO(name) (&name::ms_classInfo)

#define wxIS_KIND_OF(obj, className) obj->IsKindOf(&className::ms_classInfo)


#define wxIsKindOf(obj, className) obj->IsKindOf(&className::ms_classInfo)

#define wxDynamicCast(obj, className) \
  ((className *) wxCheckDynamicCast( \
                                     wx_const_cast(wxObject *, wx_static_cast(const wxObject *, \
                                         wx_const_cast(className *, wx_static_cast(const className *, obj)))), \
                                     &className::ms_classInfo))



#define wxDynamicCastThis(className) \
  (IsKindOf(&className::ms_classInfo) ? (className *)(this) : (className *)0)


#define wxStaticCast(obj, className) \
  wx_const_cast(className *, wx_static_cast(const className *, obj))

#if defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING
#error mmmm


#define _WX_WANT_NEW_SIZET_WXCHAR_INT


#ifndef __VISAGECPP__
#error mmmm
#define _WX_WANT_DELETE_VOID
#endif

#if defined(__VISAGECPP__) && __DEBUG_ALLOC__
#error mmmm
#define _WX_WANT_DELETE_VOID_CONSTCHAR_SIZET
#endif


#if (defined(__VISUALC__) && (__VISUALC__ >= 1200)) || \
(defined(__MWERKS__) && (__MWERKS__ >= 0x2400))
#error mmmm
#define _WX_WANT_DELETE_VOID_WXCHAR_INT
#endif


#if wxUSE_ARRAY_MEMORY_OPERATORS


#if !defined(__VISUALC__)
#error mmmm
#define _WX_WANT_ARRAY_NEW_SIZET_WXCHAR_INT
#endif


#if !defined(__VISUALC__)
#error mmmm
#define _WX_WANT_ARRAY_DELETE_VOID
#endif


#if defined(__MWERKS__) && (__MWERKS__ >= 0x2400)
#error mmmm
#define _WX_WANT_ARRAY_DELETE_VOID_WXCHAR_INT
#endif

#endif

#endif

class wxObjectRefData {
    friend class wxObject;

  public:
    wxObjectRefData() : m_count( 1 ) { }
    virtual ~wxObjectRefData() { }

    int GetRefCount() const { return m_count; }

  private:
    int m_count;
};

class wxObject {
  public:
    static wxClassInfo ms_classInfo;
    virtual wxClassInfo *GetClassInfo() const;
  public:
    wxObject() { m_refData = NULL; }
    virtual ~wxObject() { UnRef(); }
    wxObject( const wxObject& other ) {
      m_refData = other.m_refData;
      if( m_refData ) {
        m_refData->m_count++;
      }
    }
    wxObject& operator=( const wxObject& other ) {
      if( this != &other ) {
        Ref( other );
      }
      return *this;
    }

    bool IsKindOf( wxClassInfo *info ) const;

    #ifdef _WX_WANT_NEW_SIZET_WXCHAR_INT
#error mmmmm
    void *operator new( size_t size, const wxChar *fileName = NULL, int lineNum = 0 );
    #endif

    #ifdef _WX_WANT_DELETE_VOID
#error mmmmm
    void operator delete( void * buf );
    #endif

    #ifdef _WX_WANT_DELETE_VOID_CONSTCHAR_SIZET
#error mmmmm
    void operator delete( void *buf, const char *_fname, size_t _line );
    #endif

    #ifdef _WX_WANT_DELETE_VOID_WXCHAR_INT
#error mmmmm
    void operator delete( void *buf, const wxChar*, int );
    #endif

    #ifdef _WX_WANT_ARRAY_NEW_SIZET_WXCHAR_INT
#error mmmmm
    void *operator new[]( size_t size, const wxChar *fileName = NULL, int lineNum = 0 );
    #endif

    #ifdef _WX_WANT_ARRAY_DELETE_VOID
#error mmmmm
    void operator delete[]( void *buf );
    #endif

    #ifdef _WX_WANT_ARRAY_DELETE_VOID_WXCHAR_INT
#error mmmmm
    void operator delete[]( void* buf, const wxChar*, int );
    #endif
    wxObjectRefData *GetRefData() const { return m_refData; }
    void SetRefData( wxObjectRefData *data ) { m_refData = data; }
    void Ref( const wxObject& clone );
    void UnRef();
    void UnShare() { AllocExclusive(); }
    bool IsSameAs( const wxObject& o ) const { return m_refData == o.m_refData; }
  protected:
    void AllocExclusive();
    virtual wxObjectRefData *CreateRefData() const;
    virtual wxObjectRefData *CloneRefData( const wxObjectRefData *data ) const;
    wxObjectRefData *m_refData;
};

inline wxObject *wxCheckDynamicCast( wxObject *obj, wxClassInfo *classInfo ) {
  return obj && obj->GetClassInfo()->IsKindOf( classInfo ) ? obj : NULL;
}

#define WXDEBUG_NEW new

#endif
